package emulator.hardware.nmos6502.operands;

import emulator.assembler.SymbolTable;
import emulator.hardware.HwByte;
import emulator.hardware.HwWord;
import emulator.hardware.debug.BusWatchException;
import emulator.hardware.memory.UnmappedMemoryException;
import emulator.hardware.nmos6502.Cpu6502;
import emulator.hardware.nmos6502.Operand;
import emulator.support.DisplacementOperandEval;
import emulator.support.OperandEval;
import emulator.util.MemoryPtr;

public class Displacement implements Operand {

	private int condition_bit = 0;
	private boolean target;
	private boolean will_branch;
	private int displacement;
	private int pc;
	
	public Displacement(int condition_bit, boolean target)
	{ 
		super();
		this.condition_bit = condition_bit;
		this.target = target;
	}
	
	@Override
	public void init(Cpu6502 cpu) throws UnmappedMemoryException, BusWatchException {
		displacement = cpu.fetchProgramByte();
		pc = cpu.getPC();
		will_branch = (cpu.getFlags().getBit(condition_bit) == target);
	}

	@Override
	public HwByte getByte() throws OperandException, UnmappedMemoryException {
		return new HwByte(displacement);
	}

	@Override
	public String getString() {
		HwWord pc_n = getJumpTarget();
		return "$"+(pc_n.toString());
	}

	public HwWord getJumpTarget() {
		int jumprel = displacement;
		if (jumprel > 127)
			jumprel -= 256;
		HwWord pc_n = new HwWord(pc);
		pc_n.inc(jumprel);
		return pc_n;
	}
	
	@Override
	public HwWord getWord() throws OperandException {
		throw new OperandException("getWord() on displacement called", this);
	}

	@Override
	public void setByte(HwByte data) throws OperandException {
		throw new OperandException("setByte() on displacement called", this);
	}

	@Override
	public void setWord(HwWord data) throws OperandException {
		throw new OperandException("setWord() on displacement called", this);
	}

	@Override
	public int getByteCount() {
		return 1;
	}

	@Override
	public String getString(MemoryPtr mem_ref, SymbolTable symbols) {
		return symbols.createJumpLabel(mem_ref.getRelativeOp());
	}

	@Override
	public int getExtraCycles() {
		int cycles = 0;
		if (will_branch)
		{
			cycles++;
			int address = (int) getJumpTarget().getNumber();
			if (((address ^ pc) & 0xFF00) != 0)
				cycles++;
		}
		return cycles;
	}

	@Override
	public OperandEval getEvaluator(MemoryPtr mem_ref) {
		return new DisplacementOperandEval(mem_ref);
	}

}
